package com.hb.webapp.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.MapBindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.hb.Constants;
import com.hb.model.JsonResult;
import com.hb.model.Role;
import com.hb.model.User;
import com.hb.service.RoleManager;
import com.hb.service.UserExistsException;
import com.hb.service.UserManager;
import com.hb.webapp.util.RequestUtil;

/**
 * Implementation of <strong>SimpleFormController</strong> that interacts with
 * the {@link UserManager} to retrieve/persist values to the database.
 *
 * <p><a href="UserFormController.java.html"><i>View Source</i></a>
 *
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
 */
@Controller
public class UserFormController extends BaseFormController {
    private static final String FN = "fn";
    private static final String EMAIL = "email";
    private static final String PWD = "pwd";
	private RoleManager roleManager;

    @Autowired
    public void setRoleManager(RoleManager roleManager) {
        this.roleManager = roleManager;
    }

    public UserFormController() {
        setCancelView("redirect:/mainMenu");
        setSuccessView("redirect:/admin/users");
    }

    @RequestMapping(method = RequestMethod.POST, value="/editacc{type}")
    public @ResponseBody JsonResult editacc(@PathVariable String type, User user, BindingResult errors, HttpServletRequest request, HttpServletResponse response) throws Exception{
    	log.debug("entering 'editacc' method...");
    	log.debug("acc setting type is " + type);
    	User cleanUser = getUserManager().getUserByUsername(request.getRemoteUser());
    	JsonResult rs = new JsonResult("", true);
    	Locale locale = request.getLocale();
    	
    	if(FN.equals(type)){
	    	cleanUser.setFirstName(user.getFirstName());
	    	log.debug("set firstname: " + user.getFirstName());
	    	cleanUser.setLastName(user.getLastName());
	    	log.debug("set lastname: " + user.getLastName());
    	}else {
    		if(getUserManager().checkPassword(cleanUser, user.getPassword())){
    			log.debug("editacc: matching password");
    		}else{
    			log.debug("editacc: mismatching password");
    			rs.setMessage(getText("errors.password.invalid", locale));
    			rs.setStatus(false);
    			return rs;
    		}
    		if(EMAIL.equals(type)){
    			cleanUser.setEmail(user.getEmail());
    		}else if (PWD.equals(type)){
    			cleanUser.setPassword(user.getNewPassword());
    		}
    	}
    	
    	if (validator != null) { // validator is null during testing
    		
    		cleanUser.setConfirmPassword(cleanUser.getPassword());
            validator.validate(cleanUser, errors);

            if (errors.hasErrors()) { // don't validate when deleting
            	log.error(errors.toString());
            	rs.setMessage(getText("errors.save", locale));
            	rs.setStatus(false);
                return rs;
            }
        }
    	
        try {
            getUserManager().saveUser(cleanUser);
        } catch (AccessDeniedException ade) {
            // thrown by UserSecurityAdvice configured in aop:advisor userManagerSecurity
            log.warn(ade.getMessage());
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
        } catch (Exception e){
        	rs.setMessage(getText("errors.save", locale));
        	rs.setStatus(false);
        }
        
        return rs;
    }
    
    @RequestMapping(method=RequestMethod.POST,value="/editbasicinfo")
    public JsonResult editBasicInfo(User user, BindingResult errors, HttpServletRequest request, HttpServletResponse response) throws Exception{
    	if(errors.hasErrors()){
    		log.error(errors.getAllErrors());
    	}
    	User cleanUser = getUserManager().getUserByUsername(request.getRemoteUser());
    	JsonResult rs = new JsonResult("", true);
    	Locale locale = request.getLocale();
    	
    	cleanUser.setGender(user.getGender());
    	cleanUser.setDob(user.getDob());
    	cleanUser.setAddress(user.getAddress());
    	cleanUser.setPhoneNumber(user.getPhoneNumber());
    	try {
    		//getUserManager().save(cleanUser);
    	} catch (AccessDeniedException ade){
    		log.warn(ade.getMessage());
    		response.sendError(HttpServletResponse.SC_FORBIDDEN);
    	} catch (Exception e){
    		rs.setMessage(getText("errors.save", locale));
        	rs.setStatus(false);
    	}
    	return rs;
    }
    
    @RequestMapping(method = RequestMethod.POST, value="/userform*")
    public String onSubmit(User user, BindingResult errors, HttpServletRequest request,
                           HttpServletResponse response)
            throws Exception {
        if (request.getParameter("cancel") != null) {
            if (!StringUtils.equals(request.getParameter("from"), "list")) {
                return getCancelView();
            } else {
                return getSuccessView();
            }
        }

        if (validator != null) { // validator is null during testing
            validator.validate(user, errors);

            if (errors.hasErrors() && request.getParameter("delete") == null) { // don't validate when deleting
                return "userform";
            }
        }

        log.debug("entering 'onSubmit' method...");

        Locale locale = request.getLocale();

        if (request.getParameter("delete") != null) {
            getUserManager().removeUser(user.getId().toString());
            saveMessage(request, getText("user.deleted", user.getFullName(), locale));

            return getSuccessView();
        } else {

            // only attempt to change roles if user is admin for other users,
            // showForm() method will handle populating
            if (request.isUserInRole(Constants.ADMIN_ROLE)) {
                String[] userRoles = request.getParameterValues("userRoles");

                if (userRoles != null) {
                    user.getRoles().clear();
                    for (String roleName : userRoles) {
                        user.addRole(roleManager.getRole(roleName));
                    }
                }
            } else {
                // if user is not an admin then load roles from the database
                // (or any other user properties that should not be editable 
                // by users without admin role) 
                User cleanUser = getUserManager().getUserByUsername(
                        request.getRemoteUser());
                user.setRoles(cleanUser.getRoles());
            }

            Integer originalVersion = user.getVersion();

            try {
                getUserManager().saveUser(user);
            } catch (AccessDeniedException ade) {
                // thrown by UserSecurityAdvice configured in aop:advisor userManagerSecurity
                log.warn(ade.getMessage());
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
                return null;
            } catch (UserExistsException e) {
                errors.rejectValue("username", "errors.existing.user",
                        new Object[]{user.getUsername(), user.getEmail()}, "duplicate user");

                // redisplay the unencrypted passwords
                user.setPassword(user.getConfirmPassword());
                // reset the version # to what was passed in
                user.setVersion(originalVersion);

                return "userform";
            }

            if (!StringUtils.equals(request.getParameter("from"), "list")) {
                saveMessage(request, getText("user.saved", user.getFullName(), locale));

                // return to main Menu
                return getCancelView();
            } else {
                if (StringUtils.isBlank(request.getParameter("version"))) {
                    saveMessage(request, getText("user.added", user.getFullName(), locale));

                    // Send an account information e-mail
                    message.setSubject(getText("signup.email.subject", locale));

                    try {
                        sendUserMessage(user, getText("newuser.email.message", user.getFullName(), locale),
                                RequestUtil.getAppURL(request));
                    } catch (MailException me) {
                        saveError(request, me.getCause().getLocalizedMessage());
                    }

                    return getSuccessView();
                } else {
                    saveMessage(request, getText("user.updated.byAdmin", user.getFullName(), locale));
                }
            }
        }

        return "userform";
    }

    @ModelAttribute
    @RequestMapping(method = RequestMethod.GET, value="/userform*")
    protected User showForm(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // If not an administrator, make sure user is not trying to add or edit another user
        if (!request.isUserInRole(Constants.ADMIN_ROLE) && !isFormSubmission(request)) {
            if (isAdd(request) || request.getParameter("id") != null) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
                log.warn("User '" + request.getRemoteUser() + "' is trying to edit user with id '" +
                        request.getParameter("id") + "'");

                throw new AccessDeniedException("You do not have permission to modify other users.");
            }
        }

        if (!isFormSubmission(request)) {
            String userId = request.getParameter("id");

            // if user logged in with remember me, display a warning that they can't change passwords
            log.debug("checking for remember me login...");

            AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
            SecurityContext ctx = SecurityContextHolder.getContext();

            if (ctx.getAuthentication() != null) {
                Authentication auth = ctx.getAuthentication();

                if (resolver.isRememberMe(auth)) {
                    request.getSession().setAttribute("cookieLogin", "true");

                    // add warning message
                    saveMessage(request, getText("userProfile.cookieLogin", request.getLocale()));
                }
            }

            User user;
            if (userId == null && !isAdd(request)) {
                user = getUserManager().getUserByUsername(request.getRemoteUser());
            } else if (!StringUtils.isBlank(userId) && !"".equals(request.getParameter("version"))) {
                user = getUserManager().getUser(userId);
            } else {
                user = new User();
                user.addRole(new Role(Constants.USER_ROLE));
            }

            user.setConfirmPassword(user.getPassword());

            return user;
        } else {
            // populate user object from database, so all fields don't need to be hidden fields in form
            return getUserManager().getUser(request.getParameter("id"));
        }
    }

    private boolean isFormSubmission(HttpServletRequest request) {
        return request.getMethod().equalsIgnoreCase("post");
    }

    protected boolean isAdd(HttpServletRequest request) {
        String method = request.getParameter("method");
        return (method != null && method.equalsIgnoreCase("add"));
    }
}
